module Forwarding (
    
    input       [0:0]   clock,
    input       [0:0]   reset,

    // Stall
	output		[0:0]   fw_if_id_stall,

	//Decode
	input       [4:0]   id_fw_regdest,
	input       [0:0]   id_fw_load,   
	input       [4:0]   id_fw_addra,  
	input       [4:0]   id_fw_addrb,  
	input      [31:0]   id_fw_rega,   
	input      [31:0]   id_fw_regb,   
	output reg [31:0]   fw_id_rega,   
	output reg [31:0]   fw_id_regb,

    // Execute
    input      [31:0]   ex_fw_wbvalue,	
    input       [0:0]   ex_fw_writereg,	

    // Memory
    input       [31:0]  mem_fw_wbvalue,	
    input        [0:0]  mem_fw_writereg,

    // Writeback
    input       [31:0]  wb_fw_wbvalue,	
    input        [0:0]  wb_fw_writereg	
    
    );
    
    // Queue of destination addresses; stores new adresses
    // whenever a new instruction gets through the decode stage
	reg [4:0] TableFW [3:0];

    // If the instruction executing is a load and there is any data
    // dependency a stall is introduced
    assign fw_if_id_stall = (id_fw_load && 
        ((id_fw_addra == TableFW[0]) ||
        (id_fw_addrb == TableFW[0]))) ? 1'b1 : 1'b0;
    
    // Reset
    always @(posedge(reset)) begin
        
        TableFW [0] <= 5'b00000;
        TableFW [1] <= 5'b00000;
        TableFW [2] <= 5'b00000;
        TableFW [3] <= 5'b00000;
        fw_id_rega  <= 32'b0;
        fw_id_regb  <= 32'b0;

    end

    // Forwarding
    always @(posedge(clock)) begin

        if (id_fw_load == 1'b0) begin
            
            // Addresses
            TableFW[0] <= id_fw_regdest;
            TableFW[1] <= TableFW[0];
            TableFW[2] <= TableFW[1];
            TableFW[3] <= TableFW[2];
            
            // Register A
            if (TableFW[0] == id_fw_addra) begin
                
                // Execute
                if (ex_fw_writereg == 1'b1) begin
                    fw_id_rega <= ex_fw_wbvalue;
                end else begin
                    fw_id_rega <= id_fw_rega;
                end
     
            end else if (TableFW[1] == id_fw_addra) begin
                
                // Memory
                if (mem_fw_writereg == 1'b1) begin
                    fw_id_rega <= mem_fw_wbvalue;
                end else begin
                    fw_id_rega <= id_fw_rega;
                end
                
            end else if (TableFW[2] == id_fw_addra) begin
                
                // Writeback
                if (wb_fw_writereg == 1'b1) begin                    
                    fw_id_rega <= wb_fw_wbvalue;
                end else begin
                    fw_id_rega <= id_fw_rega;
                end
                
            end else begin

                fw_id_rega <= id_fw_rega;

            end
            
            // Register B
            if (TableFW[0] == id_fw_addrb) begin
                
                // Execute
                if (ex_fw_writereg == 1'b1) begin
                    fw_id_regb <= ex_fw_wbvalue;
                end else begin
                    fw_id_regb <= id_fw_regb;
                end
                
            end else if (TableFW[1] == id_fw_addrb) begin
                
                // Memory
                if (mem_fw_writereg == 1'b1) begin
                    fw_id_regb <= mem_fw_wbvalue;
                end else begin
                    fw_id_regb <= id_fw_regb;
                end
                
            end else if (TableFW[2] == id_fw_addrb) begin
                
                // Writeback
                if (wb_fw_writereg == 1'b1) begin                    
                    fw_id_regb <= wb_fw_wbvalue;
                end else begin
                    fw_id_regb <= id_fw_regb;
                end
                
            end else begin

                fw_id_regb <= id_fw_regb;

            end
            
        end

    end

endmodule
